﻿using System;
using System.Text;
using System.Collections.Generic;
using System.IO;

namespace edu.und.crc.clients.chemistry.macrodox
{
    struct DoubleVector
    {
        public double x;
        public double y;
        public double z;

        public DoubleVector(double X, double Y, double Z)
        {
            x = X;
            y = Y;
            z = Z;
        }
    }

    struct Settings
    {
        public string stID;

        public void initData()
        {
            stID = "Settings";
        }
    }

    struct Sbir
    {
        //This is the primary data structure output eventually as the 'MDX' file.

        public string stID;

        //Maximum number of atoms permitted in a protein or nucleic acid array
        public int ma;

        //Coordinates of atom where DoubleVector array contains the x,y,z positions of the indexed molecule
        DoubleVector[] xv;

        double[] charge;

        /* 
         * Atomic mass of indexed atom in amu. These are assigned upon reading in a 
         * new coordinate file with the NEW command. This is done by the 
         * assign() method. Mass values are determined by the parameter file
         * 'macprep.chg'.
         */
        public double[] ams;

        /*
         * distance of index atom from center of mass of the molecule.
         * These are computed by the dist() method when reading in a new coordinate
         * set with the NEW command.
         */
        public double[] distance;

        //Maximum formal charge on the indexed atom for Tanford-Kirkwood calculation.
        public double[] tchg;

        //Recommended mean charge on the indexed atom after Tan-Kirk calculation
        public double[] achg;

        //c  PKINT(I) = intrinsic pKa of a titratible site (0 for other atoms)
        public int[] pkint;

        //c  BUR(I) = burial ratio of indexed atom (only those with TCHG ne 0)
        public double[] bur;

        //c  TAC(I) = surface accessibility of indexed atom after Richards' or Quik surface cal
        public double[] tac;

        //c  DPL = dipole moment magnitude of molecule with charges in array CHARGE
        public double dpl;

        //c  DIP(1..3) = dipole moment vector    "     "      "    "       "   "     "
        public DoubleVector dip;

        //c  TI(1..3) = normal vector of heme plane, if applicable
        public DoubleVector ti;

        //c  CM(1..3) = center of mass of molecule
        public DoubleVector cm;

        //c  RP(1..3) = center of positive charge
        public DoubleVector rp;

        //c  RN(1..3) = center of negative charge
        public DoubleVector rn;

        //c  TP = total positive charge
        public double tp;

        //c  TN = total negative charge
        public double tn;

        //c  GREATEST = distance from center of mass to atom furthest out.
        public double greatest;

        //c  RGYR = radius of gyration of molecule
        public double rgyr;

        //c  RAD = estimated spherical radius of the molecule
        public double rad;

        //c  DTRANS = Stokes-Einstein estimated translational diffusion coefficient
        public double dTrans;

        //c  DROT = Stokes-Einstein estimated rotational diffusion coefficient
        public double dRot;

        //c  SPECI(I) = species type of indexed atom (ATOM, HETATM, TER, etc)
        public string[] speci;

        //c  ANAME(I) = atom name of indexed atom (CA, C, N, O, CB, etc)
        public string[] aName;

        //c  RESNAME(I) = residue name of indexed atom (GLY, HIS, PRO, etc)
        public string[] resName;

        //c  Q(I) = segment designator of indexed atom (for multi-subunit proteins)
        public string[] q;

        //c  COMST =
        public string comst;

        //c  NATOM = total number of atoms in the molecule
        public int nAtom;

        //c  INUMBER(I) = atom sequence number of indexed atom (a number from 1 to NATOM)
        public int[] iNumber;

        //c  IRESNUM(I) = residue number of indexed atom
        public int[] iResNum;

        //c  IHEME = flag to say whether the protein is a heme protein (1=Yes)
        public bool iheme;

        //c  NFE = atom sequence number of FE atom in a heme-containing protein,
        //c        or the "monitor atom" in general (in BD runs)
        public int nfe;

        //c  NCHA, NCHB, NCHC, NCHD = atom sequence numbers of heme peripheral atoms
        //c      in a heme-containing protein, or the "reactive atom set" in general
        //c      (in BD runs).
        public int ncha, nchb, nchc, nchd;

        //c  IRESOX=residue number containing the OXY terminus
        public int iResOX;

        //c  ICOM=
        //Not sure if this is to be an int or a bool
        public bool iCom;

        //c  IJOU=flag which tells whether a full surface calculation has been performed.
        public bool iJou;

        //c  IRUN=obsolete;
        //c       journal-run flag (=1 when running a script of commands in journal file)
        public bool iRun;

        //c  IH=hydrogen flag (=1 when hydrogens are present in the protein file)
        public bool ih;

        public void initData(int max)
        {
            stID = "Sbir";
            ma = max;
            xv = new DoubleVector[ma];
            charge = new double[ma];
            ams = new double[ma];
            distance = new double[ma];
            tchg = new double[ma];
            achg = new double[ma];
            pkint = new int[ma];
            bur = new double[ma];
            tac = new double[ma];
            dpl = 0;
            dip = new DoubleVector();
            ti = new DoubleVector();
            cm = new DoubleVector();
            rp = new DoubleVector();
            rn = new DoubleVector();
            tp = 0;
            tn = 0;
            greatest = 0;
            rgyr = 0;
            rad = 0;
            dTrans = 0;
            dRot = 0;
            nAtom = 0;
            iNumber = new int[ma];
            iResNum = new int[ma];
            iheme = false;
            nfe = 0;
            ncha = 0;
            nchb = 0;
            nchc = 0;
            nchd = 0;
            iResOX = 0;
            iCom = false;
            iJou = false;
            iRun = false;
            ih = false;
            q = new string[ma]; //1 char in fortran
            aName = new string[ma]; //4 char in fortran
            resName = new string[ma];//4 char in fortran
            speci = new string[ma];//6 char in fortran
            comst = "";//50 char in fortran
        }
    }

    struct DataBlock
    {
        //Keeps Track of current residue and section information
        public string glSection;// = "A";
        public int glSecResi;// = 0;
        public int glSecAtom;// = 0;

        public string remark;// = "";
        public List<int> secAtoms;// = new List<int>();
        public List<string> atomNames;// = new List<string>();
        public string nameRes;// = "";
        public string section;// = "";
        public int secResi;// = 0;
        public List<DoubleVector> coords;// = new List<DoubleVector>();
        public List<double> x;// = new List<double>();
        public List<double> y;// = new List<double>();
        public List<string> tAtom;// = new List<string>();

        public MutateData md;
        public ReplaceData rd;

        public void setMD(ref MutateData MD)
        {
            this.md = MD;
        }

        public void setRP(ref ReplaceData RD)
        {
            this.rd = RD;
        }

        public void readFile(string mdxPath)
        {//45-11

            glSection = "A";
            glSecResi = 0;
            glSecAtom = 0;

            remark = "";
            secAtoms = new List<int>();
            atomNames = new List<string>();
            nameRes = "";
            section = "";
            secResi = 0;
            coords = new List<DoubleVector>();
            x = new List<double>();
            y = new List<double>();
            tAtom = new List<string>();

            
            string bkPath = mdxPath + ".bk";
            Console.WriteLine("Backing Up: " + mdxPath);
            Console.WriteLine("To: " + bkPath);
            try
            {
                File.Copy(mdxPath, bkPath);
            }
            catch (Exception e)
            {
                Console.Clear();
                Console.Write("The following file already exists:\n" + bkPath + "\n\nDo you wish to overwrite it? [yes/no]: ");
                string answer = Console.ReadLine();
                answer = answer.Trim().ToLower();
                if(answer.Equals("y") || answer.Equals("yes"))
                {
                    File.Delete(bkPath);
                    File.Copy(mdxPath, bkPath);
                }
                else if (answer.Equals("n") || answer.Equals("no"))
                {
                    Console.Clear();
                    Console.WriteLine("No - Press 'Enter' to return to the main menu...");
                    return;
                }
                else
                {
                    Console.Clear();
                    Console.WriteLine("MacroDox did not understand your input: {0}\n\nPress 'Enter' to return to the main menu...",answer);
                    return;
                }
            }
            StreamReader sr = new StreamReader(bkPath);
            StreamWriter sw = new StreamWriter(mdxPath, false);

            int last = 0;

            do
            {
                string line = sr.ReadLine();
                if (line.Length > 5)
                {
                    string lrem = line.Substring(0, 6).ToUpper();
                    if (!lrem.Equals("ATOM  ") && !lrem.Equals("HETATM"))
                    {
                        purgeMe(ref sw, true);
                        sw.WriteLine(line);
                    }
                    else
                    {
                        string latm = line.Substring(13, 4).ToUpper();
                        string lres = line.Substring(17, 4).ToUpper();
                        string lsec = line.Substring(21, 1).ToUpper();
                        int lsecRes = Int32.Parse(line.Substring(22, 4));
                        DoubleVector lvec = new DoubleVector(Double.Parse(line.Substring(30, 8)), Double.Parse(line.Substring(38, 8)), Double.Parse(line.Substring(46, 8)));
                        double lx = Double.Parse(line.Substring(54, 6));
                        double ly = Double.Parse(line.Substring(60, 6));
                        string ltAtom = line.Substring(66);

                        if (last != 0 && lsecRes != last) purgeMe(ref sw, false);
                        else if (last == 0)
                        {
                            glSecAtom = 1;
                            glSecResi = 1;
                        }
                        else if (!glSection.Equals(lsec)) glSecAtom = 1;

                        last = lsecRes;

                        remark = lrem;
                        atomNames.Add(latm);
                        nameRes = lres;
                        glSection = lsec;
                        glSecResi = lsecRes;
                        coords.Add(lvec);
                        x.Add(lx);
                        y.Add(ly);
                        tAtom.Add(ltAtom);
                    }
                }
                else
                {
                    purgeMe(ref sw, true);
                    sw.WriteLine(line);
                }
            }
            while (!sr.EndOfStream);
            purgeMe(ref sw, true);
            Console.WriteLine("\nFile Traversal Complete - Press 'Enter' to return to the main menu...");
//            Console.ReadLine();
            sr.Close();
            sw.Close();
        }

        public void clearData()
        {
            secAtoms.Clear();
            atomNames.Clear();
            coords.Clear();
            x.Clear();
            y.Clear();
            tAtom.Clear();
        }

        public void purgeMe(ref StreamWriter sw, bool ignoreIt)
        {
            int oldIndex = -1;
            for (int i = 0; i < md.nameRes.Length; i++)
            {
                if (nameRes.Trim().Equals(md.nameRes[i].Trim()))
                {
                    oldIndex = i;
                    break;
                }
            }
            if (oldIndex >= 0)
            {
                int index = rd.getIndexOfMatch(ref nameRes);
                if (index >= 0)
                {
                    if (rd.hasMatchSection(glSection, index))
                    {
                        if (rd.hasMatchRange(glSecResi, index))
                        {
                            string newname = rd.getNewResidue(index);

                            int nrIndex = 0;
                            for (int i = 0; i < md.nameRes.Length; i++)
                            {
                                if (newname.Trim().Equals(md.nameRes[i].Trim()))
                                {
                                    nrIndex = i;
                                    break;
                                }
                            }
                            writeNewResidue(oldIndex, nrIndex, ref sw);
                        }
                        else writeOldResidue(ref sw);//no match
                    }
                    else writeOldResidue(ref sw);//no match
                }
                else writeOldResidue(ref sw);//no match
            }
            else if(!ignoreIt)
            {
                Console.WriteLine("Old residue information not found within the Residue Filter file");
                writeOldResidue(ref sw);//no match
            }
            clearData();
        }

        public void writeOldResidue(ref StreamWriter sw)
        {
            //ref StreamWriter sw, ref int secResi, ref string section, ref int glSecAtom, 
            int rlen = atomNames.Count;
            for (int i = 0; i < rlen; i++)
            {
                StringBuilder sb = new StringBuilder(80);
                sb.AppendFormat(remark);
                sb.AppendFormat("{0,5:D}  ", glSecAtom);
                glSecAtom = glSecAtom + 1;
                sb.AppendFormat("{0,-4}", atomNames[i]);
                sb.AppendFormat("{0,-4}", nameRes);
                sb.AppendFormat(glSection);
                sb.AppendFormat("{0,4}    ", glSecResi);

                sb.AppendFormat("{0,8:F3}{1,8:F3}{2,8:F3}", coords[i].x, coords[i].y, coords[i].z);

                sb.AppendFormat("{0,6:F2}{1,6:F2}", x[i], y[i]);
                sb.AppendFormat("{0,12}  ", atomNames[i].Substring(0, 1));
                sw.WriteLine(sb.ToString());
            }
        }

        public void writeNewResidue(int oldIndex, int nrIndex, ref StreamWriter sw)
        {
            //ref StreamWriter sw, ref int secResi, ref string section, ref int glSecAtom, 
            DoubleVector dv = md.strcrds[oldIndex][0];
            dv.x = dv.x - coords[0].x;
            dv.y = dv.y - coords[0].y;
            dv.z = dv.z - coords[0].z;
            for (int i = 0; i < md.nitr[nrIndex]; i++)
            {
                StringBuilder sb = new StringBuilder(80);
                sb.AppendFormat(remark);
                sb.AppendFormat("{0,5:D}  ", glSecAtom);
                glSecAtom = glSecAtom + 1;
                sb.AppendFormat("{0,-4}", md.atoms[nrIndex][i]);
                sb.AppendFormat("{0,-4}", md.nameRes[nrIndex]);
                sb.AppendFormat(glSection);
                sb.AppendFormat("{0,4}    ", glSecResi);

                //Not adjusting for positioning
                sb.AppendFormat("{0,8:F3}{1,8:F3}{2,8:F3}",md.strcrds[nrIndex][i].x+dv.x,md.strcrds[nrIndex][i].y+dv.y,md.strcrds[nrIndex][i].z+dv.z);

                //Just putting in zeros
                sb.AppendFormat("{0,6:F2}{1,6:F2}", 0.00, 0.00);
                sb.AppendFormat("{0,12}  ", md.atoms[nrIndex][i].Substring(0, 1));
                sw.WriteLine(sb.ToString());
            }
        }
    }

    struct ReplaceData
    {
        public List<string> oldn;// = new List<string>();
        public List<string> newn;// = new List<string>();
        public List<string> start;// = new List<string>();
        public List<string> stop;// = new List<string>();
        public List<int> range1;// = new List<int>();
        public List<int> range2;// = new List<int>();

        public void readValues(string path)
        {
            oldn = new List<string>();
            newn = new List<string>();
            start = new List<string>();
            stop = new List<string>();
            range1 = new List<int>();
            range2 = new List<int>();

            StreamReader srr = new StreamReader(path);
            //Throw File Header
            srr.ReadLine();

            //Get Replacement Options Length
            int nReps = Int32.Parse(srr.ReadLine());

            //Throw Divider
            srr.ReadLine();

            //Throw Column Headings
            srr.ReadLine();

            //Read Replacement Options
            for (int i = 0; i < nReps; i++)
            {
                string line = srr.ReadLine();
                oldn.Add(line.Substring(0, 8).ToUpper().Trim());
                newn.Add(line.Substring(8, 8).ToUpper().Trim());
                start.Add(line.Substring(16, 8).ToUpper().Trim());
                stop.Add(line.Substring(24, 8).ToUpper().Trim());
                range1.Add(Int32.Parse(line.Substring(32, 8)));
                range2.Add(Int32.Parse(line.Substring(40, 8)));
            }
            srr.Close();
        }

        public bool hasMatchingName(ref string on)
        {
            return oldn.Contains(on.Trim());
        }

        public int getIndexOfMatch(ref string on)
        {
            string[] oldna = oldn.ToArray();
//            Console.WriteLine("Old NA - "+oldna.Length);
            for (int i = 0; i < oldna.Length; i++)
            {
                if (oldna[i].Trim().Equals(on.Trim()))
                {
//                    Console.WriteLine("Found: "+oldna[i].Trim()+"***");
//                    Console.ReadLine();
                    return i;
                }
//                if (i == oldna.Length - 1)
//                {
//                    Console.WriteLine("End of Search");
//                    Console.WriteLine("***{0}***", on.Trim());
//                    Console.WriteLine("&&&{0}&&&", oldna[i].Trim());
//                }
            }
            return -1;
        }

        public bool hasMatchSection(string sec, int index)
        {
            bool hms = false;
            string[] str = start.ToArray();
            int c1 = (int)str[index].ToCharArray()[0];

            string[] stp = stop.ToArray();
            int c2 = (int)stp[index].ToCharArray()[0];

            int c3 = (int)sec.ToCharArray()[0];
            if(c3 >= c1 || c3 <= c2) hms = true;

            return hms;
        }

        public bool hasMatchRange(int r, int index)
        {
            bool hmr = false;
            int[] r1 = range1.ToArray();
            int[] r2 = range2.ToArray();
            if (r >= r1[index] || r <= r2[index]) hmr = true;
            return hmr;
        }

        public string getNewResidue(int index)
        {
            string[] names = newn.ToArray();
            return names[index];
        }
    }

    struct MutateData
    {
        // mutate.inc - global variables storing the standard residue coordinates.
        // maximum number of residues allowed
        public int maxnrsc;

        //c   nrsc = number of residues in the standard residue coordinate file
        public int nrsc;

        //c   nitr(i) = number of atoms in residue i
        public int[] nitr;

        //c   nameres(i) = name of residue i
        public string[] nameRes;

        //c   atoms(i,j) = name of atom j within residue i
//        public string[,] atoms;
        public List<string[]> atoms;

        //c   strcrds(i,j,k) = coordinates k=1,3 or atom j within residue i
//        public DoubleVector[,] strcrds;
        public List<DoubleVector[]> strcrds;


        public void initData(string path)
        {
            StreamReader sr = new StreamReader(path);
            sr.ReadLine();
            string rn = sr.ReadLine();
            maxnrsc = Int32.Parse(rn);
            nrsc = maxnrsc;
            nitr = new int[nrsc];
            nameRes = new string[nrsc];
            atoms = new List<string[]>();
            strcrds = new List<DoubleVector[]>();


            int resi = -1;
            int atomn = 0;
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
//                if (!line.Substring(14, 1).Equals("."))
                if (line.Length <= 10)
                {
                    resi = resi + 1;
                    atomn = 0;
                    nitr[resi] = Int32.Parse(line.Substring(0, 5));
                    nameRes[resi] = line.Substring(5).Trim();
                    atoms.Add(new string[nitr[resi]]);
                    strcrds.Add(new DoubleVector[nitr[resi]]);
                }
                else
                {
                    atoms[resi][atomn] = line.Substring(5, 4).Trim();
                    strcrds[resi][atomn] = new DoubleVector(Double.Parse(line.Substring(10, 8)), Double.Parse(line.Substring(18, 8)), Double.Parse(line.Substring(26, 8)));
                    atomn = atomn + 1;
                }
            }
            sr.Close();
        }
    }
}
